#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "net.h"
#include "file_manger.h"
#include "log.h"
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>

// #define MMAP 
// #define FILE_LOCK

char path[] = "./database/";
extern HashTable *hash_table;
int current_socket;


typedef enum {
    write_command,
    read_command,
    delete,
    enum_file,
    end,
    heartbeat,
}command_t;

typedef enum {
    noerror,
    error,
    busy,
    nofile,
    nosupport,
    continue_enum,
    continue_read,
    read_done,
}process_error_t;

typedef struct {
    char filename[32];
    u8 data[512];
}write_cmd_t;

typedef struct {
    char filename[32];
}delete_cmd_t;

typedef struct {
    u32 start;
    u32 size;
    char filename[32];
}read_cmd_t;

typedef union {
    write_cmd_t write_cmd;
    delete_cmd_t delete_cmd;
    read_cmd_t   read_cmd;
}cmd_type_t;

typedef struct {
    int command;
    int packet_id;
    int data_size;
    cmd_type_t cmd;
}cmd_t;



typedef struct {
    char filename[32];
}write_ack_t;

typedef struct {
    int file_num;
    u8 data[512];
}enum_file_ack_t;

typedef struct {
    u32 read_size; //实际读到的字节数
    u32 file_size;  //文件总大小
    char filename[32];
    u8 data[512];
}read_ack_t;

typedef union {
    write_ack_t write_ack;
    enum_file_ack_t enum_file_ack;
    read_ack_t read_ack;
}ack_type_t;

typedef struct {
    int cmd_ack;
    int packet_id;
    int flag;
    int data_size;
    ack_type_t ack;
}ack_t;


int write_fopen(cmd_t *rec_data) {
    int filename_len = strlen(rec_data->cmd.write_cmd.filename);
    if (filename_len == 0) {
        //错误
        return -1;
    }
    char file_path[64];
    memcpy(file_path, path, sizeof(path));
    strncat(file_path, rec_data->cmd.write_cmd.filename, filename_len);
    DBG("path is %s\n", file_path);
#ifdef MMAP
    struct stat sb;
    void *mapped;
    off_t new_size;
    int fd = open(file_path, O_RDWR|O_CREAT|O_APPEND, 0666);
    if (fd == -1) {
        perror("open error:");
        return -1;
    }

    // 获取文件信息
    if (fstat(fd, &sb) == -1) {
        perror("Error getting the file size");
        close(fd);
        return -1;
    }

    new_size = sb.st_size + rec_data->data_size;

    // 扩展文件大小
    if (ftruncate(fd, new_size) == -1) {
        perror("Error truncating the file");
        close(fd);
        return -1;
    }

    mapped = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("Error mapping the file");
        close(fd);
        return -1;
    }

    //写入数据到文件
    memcpy((char *)mapped + sb.st_size, rec_data->cmd.write_cmd.data, rec_data->data_size);

    // 刷新映射到磁盘
    if (msync(mapped, new_size, MS_SYNC) == -1) {
        perror("Error flushing the mapped data to disk");
    }

    // 释放内存映射
    if (munmap(mapped, new_size) == -1) {
        perror("Error un-mapping the file");
    }

    // 关闭文件描述符
    close(fd);

    return rec_data->data_size;

#elif defined(FILE_LOCK)
    //这一部分是文件锁，如果上一层去掉读写锁，这里的文件锁就起到了互斥的作用
    int fd = open(file_path, O_RDWR|O_CREAT|O_APPEND, 0666);
    if (fd == -1) {
        perror("open error:");
        return -1;
    }

    // 尝试对文件加锁
    if (flock(fd, LOCK_EX) == -1) {
        perror("Error locking file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    int res = write(fd, (void *)rec_data->cmd.write_cmd.data, rec_data->data_size);

    // 释放文件锁
    if (flock(fd, LOCK_UN) == -1) {
        perror("Error unlocking file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    close(fd);
    return res;

#else
     //这里应该加锁防止竞争，但是上一层有读写锁，这里不再加锁
    int res = 0;
    FILE * file = fopen(file_path, "a+");
    if (file == NULL) {
        perror("open error:");
        return -1;
    }
    // FILE * file = fopen(file_path, "w+");
    res = fwrite((void *)rec_data->cmd.write_cmd.data, 1, rec_data->data_size, file);
    fclose(file);
    return res;
#endif
}

int read_fopen(cmd_t *rec_data, ack_t* rd_ack) {
    DBG("read open\n");
    int filename_len = strlen(rec_data->cmd.read_cmd.filename);
    if (filename_len == 0) {
        //错误
        return -1;
    }
    char file_path[64];
    memcpy(file_path, path, sizeof(path));
    strncat(file_path, rec_data->cmd.read_cmd.filename, filename_len);
    DBG("path is %s\n", file_path);

#ifdef MMAP
    struct stat sb;
    void *mapped;
    // off_t new_size;
    int fd = open(file_path, O_RDONLY);
    if (fd == -1) {
        perror("open error:");
        return -1;
    }

    // 获取文件信息
    if (fstat(fd, &sb) == -1) {
        perror("Error getting the file size");
        close(fd);
        return -1;
    }

    rd_ack->ack.read_ack.file_size = sb.st_size;

    mapped = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);

    int new_size = sb.st_size - rec_data->cmd.read_cmd.start;
    if (new_size < rec_data->cmd.read_cmd.size) {
        //到文件结尾
        rd_ack->flag = read_done;
        rd_ack->data_size = 40 + new_size;
        rd_ack->ack.read_ack.read_size = new_size;
    }
    else {
        rd_ack->data_size = 40 + rec_data->cmd.read_cmd.size;
        rd_ack->ack.read_ack.read_size = rec_data->cmd.read_cmd.size;
        //没有到结尾
        rd_ack->flag = continue_read;
    }

    memcpy(rd_ack->ack.read_ack.data, ((char *)mapped) + rec_data->cmd.read_cmd.start, rd_ack->ack.read_ack.read_size);

    
    // 释放内存映射
    if (munmap(mapped, sb.st_size) == -1) {
        perror("Error un-mapping the file");
    }

    // 关闭文件描述符
    close(fd);

    return rd_ack->ack.read_ack.read_size;

#elif defined(FILE_LOCK)
    //这一部分是文件锁，如果上一层去掉读写锁，这里的文件锁就起到了互斥的作用
    int fd = open(file_path, O_RDONLY);
    if (fd == -1) {
        perror("open error:");
        return -1;
    }

    // 尝试对文件加锁
    if (flock(fd, LOCK_EX) == -1) {
        perror("Error locking file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    rd_ack->ack.read_ack.file_size = lseek(fd, 0, SEEK_END);
    lseek(fd, rec_data->cmd.read_cmd.start, SEEK_SET);
    int res = read(fd, (void *)rd_ack->ack.read_ack.data, rec_data->cmd.read_cmd.size);
    if (res < rec_data->cmd.read_cmd.size) {
            //到文件结尾
            rd_ack->flag = read_done;
            rd_ack->data_size = 40 + res;
            rd_ack->ack.read_ack.read_size = res;
    }
    else {
        rd_ack->data_size = 40 + res;
        rd_ack->ack.read_ack.read_size = res;
        //没有到结尾
        rd_ack->flag = continue_read;
    }

    // 释放文件锁
    if (flock(fd, LOCK_UN) == -1) {
        perror("Error unlocking file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    close(fd);
    return res;

#else
    int res = 0;
    //因为上一层用了读写锁，这里不再对文件描述符加锁
    FILE * file = fopen(file_path, "r");
    if (file == NULL) {
        perror("open error:");
        return -1;
    }

    //获取文件大小
    rd_ack->ack.read_ack.file_size = fseek(file, 0, SEEK_END);
    fseek(file, rec_data->cmd.read_cmd.start, SEEK_SET);
    res = fread((void *)rd_ack->ack.read_ack.data, 1, rec_data->cmd.read_cmd.size, file);
    DBG("fread res:%d\n", res);
    if (res < rec_data->cmd.read_cmd.size) {
        if (feof(file)) {
            //到文件结尾
            rd_ack->flag = read_done;
            rd_ack->data_size = 40 + res;
            rd_ack->ack.read_ack.read_size = res;
        }
        else if (ferror(file)){
            fprintf(stderr, "Error reading from file\n");
            fclose(file);
            return -1;
        }
    }
    else {
        rd_ack->data_size = 40 + res;
        rd_ack->ack.read_ack.read_size = res;
        //没有到结尾
        rd_ack->flag = continue_read;
    }

    fclose(file);
    return res;
#endif
}

void delete_fopen(cmd_t *rec_data, ack_t* de_ack) {
    int filename_len = strlen(rec_data->cmd.delete_cmd.filename);
    if (filename_len == 0) {
        //错误
        return;
    }
    char file_path[64];
    memcpy(file_path, path, sizeof(path));
    strncat(file_path, rec_data->cmd.delete_cmd.filename, filename_len);
    DBG("path is %s\n", file_path);
    //这里应该加锁防止竞争  这里用读写锁
    //长度截断为 0
    FILE * file = fopen(file_path, "w");
    fclose(file);
}

void write_cmd(cmd_t *rec_data, ack_t* wr_ack) {
    DBG("process write cmd\n");
    DBG("file name :%s, size:%d\n", rec_data->cmd.write_cmd.filename, rec_data->data_size);
    
    wr_ack->data_size = 44;
    if (rec_data->data_size == 0) {
        //结束包
        wr_ack->flag = noerror;
        return;
    }
    file_lock_t *file_lock = NULL;
    file_lock_t *res = findInHashTable(hash_table, rec_data->cmd.write_cmd.filename);
    if (res == NULL) {
        file_lock = (file_lock_t *)malloc(sizeof(file_lock_t));
        memcpy(file_lock->file_name, rec_data->cmd.write_cmd.filename, strlen(rec_data->cmd.write_cmd.filename));
        pthread_rwlock_init(&file_lock->rwlock, NULL);
        insertHashTable(hash_table, file_lock);
    }
    else {
        file_lock = res;
    }

    int wr_size = 0;
    if (!pthread_rwlock_trywrlock(&file_lock->rwlock)) {
        wr_size = write_fopen(rec_data);
        pthread_rwlock_unlock(&file_lock->rwlock);
        if (wr_size < rec_data->data_size) {
            //错误 要错误处理
            wr_ack->flag = error;
        }
        else {
            //成功
            wr_ack->flag = noerror;
        }
    }
    else {
        //file busy
        wr_ack->flag = busy;
    }
    return; 
}




void read_cmd(cmd_t *rec_data, ack_t* rd_ack) {
    DBG("process read cmd\n");
    int res;
    file_lock_t *file_lock = findInHashTable(hash_table, rec_data->cmd.read_cmd.filename);
    if (file_lock == NULL) {
        //sever have no filename
        rd_ack->flag = nofile;
        rd_ack->data_size = 40;
        DBG("hash table no file\n");
        return;
    }

    if (!pthread_rwlock_tryrdlock(&file_lock->rwlock)) {
        res = read_fopen(rec_data, rd_ack);
        if (res < 0) {
            rd_ack->data_size = 40;
            rd_ack->flag = error;
        }
    }
    else {
        rd_ack->data_size = 40;
        rd_ack->flag = busy;
    }
    return;
}

void delete_cmd(cmd_t *rec_data, ack_t* de_ack) {
    DBG("process delete cmd\n");
    DBG("file name :%s\n", rec_data->cmd.delete_cmd.filename);

    file_lock_t *file_lock = findInHashTable(hash_table, rec_data->cmd.delete_cmd.filename);
    if (file_lock == NULL) {
        //sever have no filename
        de_ack->flag = nofile;
        de_ack->data_size = 0;
        return;
    }
    
    if (!pthread_rwlock_trywrlock(&file_lock->rwlock)) {
        delete_fopen(rec_data, de_ack);
        pthread_rwlock_unlock(&file_lock->rwlock);
        de_ack->data_size = 0;
        de_ack->flag = noerror;
    }
    else {
        //file busy
        de_ack->flag = busy;
    }
    //delete in hash table
    delete_hash_node(hash_table, file_lock->file_name);
}

void enum_file_cmd(cmd_t *rec_data, ack_t* enum_ack) {
    DBG("peocess enum file\n");
    //enum_ack->ack.enum_file_ack.file_num = hash_table->file_cnt;
    HashNode *index = NULL;
    enum_ack->data_size = 4;
    enum_ack->ack.enum_file_ack.file_num = 0;
    u8 *file_names = (u8 *)&enum_ack->ack.enum_file_ack.data[0];
    for (int i = 0; i < 20; i++) {
        index = hash_table->table[i];
        while (index != NULL) {
            if ((enum_ack->data_size-4) + (strlen(index->data->file_name) + 1) >= 512) {
                enum_ack->flag = continue_enum;
                return;
            }
            memcpy(file_names, index->data->file_name, strlen(index->data->file_name));
            enum_ack->data_size += strlen(index->data->file_name);
            file_names += strlen(index->data->file_name);
            //enum_ack->ack.enum_file_ack.data[enum_ack->data_size-4] = '\n';
            *file_names = '\n';
            enum_ack->data_size++;
            file_names ++;
            enum_ack->ack.enum_file_ack.file_num ++;
            index = index->next;
        }
        
    }
    DBG("adk data size %d\n", enum_ack->data_size);
    enum_ack->flag = noerror;
}


void process(int socket) {
    int rec_len = 0;

    cmd_t *rec_data = (cmd_t *) malloc(sizeof(cmd_t));
    rec_len = net_recive(socket, (void *)rec_data);
    if (rec_len == 0) {
        //错误
        return;
    }
    else {
        DBG("recv %d bytes\n", rec_len);
        rec_data->command = ntohl(rec_data->command);
        rec_data->packet_id = ntohl(rec_data->packet_id);
        rec_data->data_size = ntohl(rec_data->data_size);
        // for (int i = 0; i < 50; i++) {
        //     DBG("%d byte:%02x\n", i, rec_data->cmd.write_cmd.data[i]);
        // }
    }

    ack_t* ack = (ack_t *) malloc(sizeof(ack_t));
    ack->cmd_ack = rec_data->command;
    ack->packet_id = rec_data->packet_id;

    switch (rec_data->command)
    {
    case write_command: {
        memcpy(ack->ack.write_ack.filename, rec_data->cmd.write_cmd.filename, 32);
        write_cmd(rec_data, ack);
        break;
    }
    case read_command: {
        rec_data->cmd.read_cmd.start = ntohl(rec_data->cmd.read_cmd.start);
        rec_data->cmd.read_cmd.size = ntohl(rec_data->cmd.read_cmd.size);
        memcpy(ack->ack.read_ack.filename, rec_data->cmd.read_cmd.filename, 32);
        ack->ack.read_ack.read_size = -1;
        read_cmd(rec_data, ack);
    }
        break;
    case delete: {
        delete_cmd(rec_data, ack);
    }
        break;
    case enum_file:
    {
        enum_file_cmd(rec_data, ack);
        //DBG("enum num:%d\n", ack->ack.enum_file_ack.file_num);
    }
        break;
    default:
        ack->flag = nosupport;
        ack->data_size = 44;
        break;
    }

    current_socket = socket;
    net_send(socket, (void *)ack, ack->data_size + 16);

    free((void *)rec_data);
    free((void *)ack);
    return;
}


